#include "ffmepgutils.h"
#include <QFile>


#define IN_DATA_SIZE 20480
#define REFILL_THRESH 4096


#define ERRMSG(NUM) char errBuf[1024]; \
    av_strerror(NUM,errBuf,sizeof (errBuf));

char inData[IN_DATA_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
char* data;

FFMEPGUtils::FFMEPGUtils()
{

}


static int checkFormat(AVCodec *codec,AVSampleFormat fmt) {


    const enum AVSampleFormat* fmts = codec->sample_fmts;

    while (*fmts != AV_SAMPLE_FMT_NONE) {

        if(*fmts == fmt) {

            return 1;
        }
        fmts++;
    }

    return 0;
}

static int encode(AVCodecContext* codecContext,AVFrame* frame,AVPacket* packet,QFile& outFile) {


    /**
     * @brief res
     *  * @return 0 on success, otherwise negative error code:
 *      AVERROR(EAGAIN):   input is not accepted in the current state - user
 *                         must read output with avcodec_receive_packet() (once
 *                         all output is read, the packet should be resent, and
 *                         the call will not fail with EAGAIN).
 *      AVERROR_EOF:       the encoder has been flushed, and no new frames can
 *                         be sent to it
 *      AVERROR(EINVAL):   codec not opened, refcounted_frames not set, it is a
 *                         decoder, or requires flush
 *      AVERROR(ENOMEM):   failed to add packet to internal queue, or similar
 *      other errors: legitimate encoding errors
     */
    int res = avcodec_send_frame(codecContext,frame);

    if (res != 0) {

        char errBuf[1024];

        av_strerror(res,errBuf,sizeof(1024));
        qDebug() << "avcodec_send_frame fail " <<  errBuf;
        return res;
    }

    while (true) {


        /**
         * Read encoded data from the encoder.
         *
         * @param avctx codec context
         * @param avpkt This will be set to a reference-counted packet allocated by the
         *              encoder. Note that the function will always call
         *              av_packet_unref(avpkt) before doing anything else.
         * @return 0 on success, otherwise negative error code:
         *      AVERROR(EAGAIN):   output is not available in the current state - user
         *                         must try to send input
         *      AVERROR_EOF:       the encoder has been fully flushed, and there will be
         *                         no more output packets
         *      AVERROR(EINVAL):   codec not opened, or it is a decoder
         *      other errors: legitimate encoding errors
         */
        res = avcodec_receive_packet(codecContext,packet);

        if(res == 0) {

            outFile.write((char *)packet->data,packet->size);
            av_packet_unref(packet);
        }else if (res == AVERROR(EAGAIN) || res == AVERROR_EOF) {

            return 0;
        }else{

            char errBuf[1024];

            av_strerror(res,errBuf,sizeof(1024));
            qDebug() << "avcodec_receive_packet fail " <<  errBuf;
            return res;
        }

    }


    return 0;
}

void FFMEPGUtils::pcmAACEncode(PCMSpec &pcmSpec)
{

    const char* fdkACC = "libfdk_aac";

    AVCodec* codec = avcodec_find_encoder_by_name(fdkACC);

    if (codec == nullptr) {

        qDebug() << "not find " << fdkACC;
        return;
    }

    if(!checkFormat(codec,pcmSpec.sampleFormat)) {

        qDebug() << av_get_sample_fmt_name(pcmSpec.sampleFormat) << " not suppert";
        return;
    }


    qDebug() << "111";

    AVCodecContext* codecContext = avcodec_alloc_context3(codec);

    if (codecContext == nullptr) {

        qDebug("avcodec_alloc_context3 fail");
        return;
    }

    codecContext->sample_fmt = pcmSpec.sampleFormat;
    codecContext->channel_layout = pcmSpec.channelLayout;
    codecContext->sample_rate = pcmSpec.sampleRate;

    // 设置AAC bit rate
//    codecContext->bit_rate = 32000;
    codecContext->profile = FF_PROFILE_AAC_HE_V2;

    AVDictionary* dict = nullptr;

    av_dict_set(&dict,"vbr","1",0);

    int res = avcodec_open2(codecContext,codec,&dict);

    if (res < 0) {

        avcodec_free_context(&codecContext);

        char errBuf[1024];

        av_strerror(res,errBuf,sizeof(errBuf));

        qDebug() <<" avcodec_open2 fail " << errBuf;
        return;
    }

   AVFrame* frame = av_frame_alloc();

   if(frame == nullptr) {

       avcodec_free_context(&codecContext);
       qDebug() << "av_frame_alloc fail";
       return;
   }

   frame->channel_layout = pcmSpec.channelLayout;
   frame->sample_rate = pcmSpec.sampleRate;
   frame->format = pcmSpec.sampleFormat;
   frame->nb_samples = codecContext->frame_size;

   res = av_frame_get_buffer(frame,0);

   if (res < 0) {

       avcodec_free_context(&codecContext);
       char errBuf[1024];
       av_strerror(res,errBuf,sizeof(errBuf));
       qDebug() <<" av_frame_get_buffer fail " << errBuf;
       return;
   }

   AVPacket* packet = av_packet_alloc();

   if (packet == nullptr) {

       av_frame_free(&frame);
       avcodec_free_context(&codecContext);

       qDebug() << "av_packet_alloc fail";
       return;
   }


   QFile inFile(pcmSpec.inFilePath);

   if(!inFile.open(QFile::ReadOnly)) {

       av_packet_free(&packet);
       av_frame_free(&frame);
       avcodec_free_context(&codecContext);
       qDebug() << pcmSpec.inFilePath << " open fail";
       return;
   }


   QFile outFile(pcmSpec.outFilePath);

   if(!outFile.open(QFile::WriteOnly)) {

       inFile.close();
       av_packet_free(&packet);
       av_frame_free(&frame);
       avcodec_free_context(&codecContext);
       qDebug() << pcmSpec.outFilePath << " open fail";
       return;
   }

   int len = 0;

   int perSampleBytes = av_get_bytes_per_sample(pcmSpec.sampleFormat) * av_get_channel_layout_nb_channels(pcmSpec.channelLayout) * pcmSpec.sampleRate;

   while ((len = inFile.read((char *)frame->data[0],frame->linesize[0])) > 0) {

        if(len < frame->linesize[0]) {

            frame->nb_samples = len / perSampleBytes;
        }

        int r = encode(codecContext,frame,packet,outFile);
        if( r != 0) {

            qDebug() << "encode fail " << r;
            break;
        }
   }


   encode(codecContext,nullptr,packet,outFile);

   outFile.close();
   inFile.close();
   av_packet_free(&packet);
   av_frame_free(&frame);
   avcodec_free_context(&codecContext);
}


static int decode(AVCodecContext* codecContext,AVPacket* packet, AVFrame* frame, QFile &outFile) {


    int res = avcodec_send_packet(codecContext,packet);

    if (res < 0) {

        ERRMSG(res);

        qDebug() << "avcodec_send_packet fail" << errBuf;
        return res;
    }

    while (true) {


        /**
         * Return decoded output data from a decoder.
         *
         * @param avctx codec context
         * @param frame This will be set to a reference-counted video or audio
         *              frame (depending on the decoder type) allocated by the
         *              decoder. Note that the function will always call
         *              av_frame_unref(frame) before doing anything else.
         *
         * @return
         *      0:                 success, a frame was returned
         *      AVERROR(EAGAIN):   output is not available in this state - user must try
         *                         to send new input
         *      AVERROR_EOF:       the decoder has been fully flushed, and there will be
         *                         no more output frames
         *      AVERROR(EINVAL):   codec not opened, or it is an encoder
         *      AVERROR_INPUT_CHANGED:   current decoded frame has changed parameters
         *                               with respect to first decoded frame. Applicable
         *                               when flag AV_CODEC_FLAG_DROPCHANGED is set.
         *      other negative values: legitimate decoding errors
         */
        res = avcodec_receive_frame(codecContext,frame);

        if(res == AVERROR(EAGAIN) || res == AVERROR(EINVAL)) {

            return 0;
        }else if(res < 0) {

            ERRMSG(res);

            qDebug() << "avcodec_receive_frame fail" << errBuf;
            return res;
        }


        outFile.write((char *)frame->data[0],frame->linesize[0]);
    }


    return 0;
}

void FFMEPGUtils::pcmAACDecode(PCMSpec &pcmSpec)
{

    const char* aacName = "libfdk_aac";
    AVCodec* codec = avcodec_find_decoder_by_name(aacName);

    if(codec == nullptr) {

        qDebug() << "not find " << aacName;
        return;
    }

    AVCodecParserContext* parserContext = av_parser_init(codec->id);

    if(parserContext == nullptr) {

        qDebug() << "av_parser_init fail ";
        return;
    }

    AVCodecContext* codecContext = avcodec_alloc_context3(codec);

    if(codecContext == nullptr) {

        qDebug() << "avcodec_alloc_context3 fail";
        return;
    }



   int res = avcodec_open2(codecContext,codec,nullptr);

   if(res != 0) {


       ERRMSG(res);
       qDebug() << "avcodec_open2 fail " << errBuf;
       return;
   }

   AVFrame* frame = av_frame_alloc();

   if(frame == nullptr) {


       avcodec_free_context(&codecContext);
       qDebug() << "av_frame_alloc fail";
       return;
   }

   AVPacket* packet = av_packet_alloc();

   if(packet == nullptr) {


       av_frame_free(&frame);
       avcodec_free_context(&codecContext);
       qDebug() << "av_packet_alloc ";
       return;
   }


    QFile inFile(pcmSpec.inFilePath);

    if(!inFile.open(QFile::ReadOnly)) {

        av_packet_free(&packet);
        av_frame_free(&frame);
        avcodec_free_context(&codecContext);
        qDebug() << "open " << pcmSpec.inFilePath << " fail";
        return;
    }


    QFile outFile(pcmSpec.outFilePath);

    if(!outFile.open(QFile::WriteOnly)) {

        inFile.close();
        av_packet_free(&packet);
        av_frame_free(&frame);
        avcodec_free_context(&codecContext);
        qDebug() << "open " << pcmSpec.outFilePath << " fail";
        return;
    }

    data = inData;
    int in_len = inFile.read(data,IN_DATA_SIZE);
    int len = 0;
    bool readFinish = false;
    while (in_len) {

        len = av_parser_parse2(parserContext,codecContext,
                               &packet->data,&packet->size,
                               (uint8_t*)data,in_len,
                               AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);

        if(len < 0) {

            ERRMSG(len);
            qDebug() << "av_parser_parse2 fail " << errBuf;
            break;
        }

        in_len -= len;
        data += len;


        if(packet->size > 0 && decode(codecContext,packet,frame,outFile) != 0) {

            qDebug() << "decode error";
            break;
        }

        if(in_len < REFILL_THRESH && !readFinish) {

            // 移动 data之后的len长度的数据到数组 inData头部
            memmove(inData,data,in_len);
            data = inData;

            int inner_len = inFile.read(data+in_len,IN_DATA_SIZE - in_len);

            if (inner_len == 0) {

               readFinish = true;
            }else {

                in_len+= inner_len;
            }
        }
    }

    // pcm参数设置
    pcmSpec.channelLayout = codecContext->channel_layout;
    pcmSpec.sampleFormat = codecContext->sample_fmt;
    pcmSpec.sampleRate = codecContext->sample_rate;

    outFile.close();
    inFile.close();
    av_packet_free(&packet);
    av_frame_free(&frame);
    avcodec_free_context(&codecContext);
}





























